home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / misc / x10-amh-.000 / x10-amh- / x10-amh-v1.04 / x10-amh.c < prev    next >
C/C++ Source or Header  |  1996-02-13  |  24KB  |  1,017 lines

  1. // Program     : x10-amh
  2. // Version     : 1.04
  3. // Description : X10 Model CP-290 control program (available at Radio Shack)
  4. //               (computer control for X10 modules)
  5. //               This application allows you to control lights and appliances
  6. //               in your home either directly, or through a crontab
  7. // Author      : Copyright (C) 1995 Aaron Hightower (aaron@paradigmsim.com)
  8. //               1217 Shirley Way
  9. //               Bedford, TX  76022 (817)267-6001
  10. //
  11. //  This program is free software; you can redistribute it and/or modify
  12. //  it under the terms of the GNU General Public License as published by
  13. //  the Free Software Foundation; either version 2 of the License, or
  14. //  (at your option) any later version.
  15. //
  16. //  This program is distributed in the hope that it will be useful,
  17. //  but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. //  GNU General Public License for more details.
  20. //
  21. //  You should have received a copy of the GNU General Public License
  22. //  along with this program; if not, write to the Free Software
  23. //  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27. #include <sys/types.h>
  28. #include <strings.h>
  29. #include <fcntl.h>
  30. #include <termios.h>
  31. #include <unistd.h>
  32. #include <time.h>
  33. #include <ctype.h>
  34.  
  35. #include "x10-amh.h"
  36.  
  37. #define X10_ON            2
  38. #define X10_OFF           3
  39. #define X10_DIM           5
  40.  
  41. #define X10_MODE_NORMAL   8
  42. #define X10_MODE_SECURITY 9
  43. #define X10_MODE_TODAY    4
  44. #define X10_MODE_TOMORROW 2
  45. #define X10_MODE_CLEAR    0
  46.  
  47. #define VERSION         "v1.04"
  48.  
  49. /*
  50.   Write n bytes from file descriptor, and don't return until done
  51.   or return -1 if failure
  52. */
  53.  
  54. static int
  55. write_n ( int fd, u_char *buf, int len )
  56. {
  57.   for ( int num=0; num<len; num++ ) {
  58.         // Inter-character delay, because the CP-290 uses a software UART and
  59.         // would drop a character while it is performing other processing.
  60.     usleep(30);
  61.  
  62.     if ( write( fd, &buf[num], 1) != 1 ) {
  63.       printf("Error writing to file: \n");
  64.  
  65.       switch ( errno ) {
  66.  
  67.         case EBADF: printf("fd is not a valid file descriptor or is not open\n"
  68.                            "for reading.\n");
  69.           return -1;
  70.  
  71.         case EINVAL: printf("fd is attached to an object which is unsuitable\n"
  72.                             "for reading.\n");
  73.           return -1;
  74.  
  75.         case EFAULT: printf("buf is outside your accessible address space.\n");
  76.           return -1;
  77.  
  78.         case EPIPE: printf("fd is connected to a pipe or socket whose reading\n"
  79.                        "end is closed. When this happens the writing pro-\n"
  80.                        "cess will receive a SIGPIPE signal; if it catches,\n"
  81.                        "blocks or ignores this the error EPIPE is returned.\n");
  82.           break;
  83.  
  84.         case EAGAIN: // Socket was unable to receive data immediately
  85.           break;
  86.  
  87.         case EINTR: // The call was interrupted by a signal
  88.           break;    // before any data was written.
  89.  
  90.         case ENOSPC: // The device containing the file referred to by fd
  91.           break;     // has no room for the data.
  92.                      // Other errors may occur, depending on the object
  93.                      // connected to fd.
  94.       }
  95.       return -1;
  96.     }
  97.   }
  98.   return len;
  99. }
  100.  
  101. /*
  102.   Read n bytes from file descriptor, and don't return until done
  103. */
  104.  
  105. static int
  106. read_n ( int fd, u_char *buf, int len )
  107. {
  108.   int num, rval;
  109.  
  110.   for ( num=0; num<len; num+=rval ) {
  111.  
  112.     if ( ( rval = read( fd, &buf[num], len-num ) ) < 0 ) rval = 0;
  113.   }
  114.  
  115.   return ( num>0 ) ? num : ( -1 );
  116. }
  117.  
  118. /*
  119.   Constructors
  120. */
  121.  
  122. x10::x10( int _fd )
  123. {
  124.   this->fd = _fd;
  125.  
  126.   listvalid = 0;
  127.  
  128.   for( int i=0; i<128; i++ ) for( int j=0; j<8; j++ ) events[i][j] = j==0 ? 255 : 0;
  129.  
  130.   config_serial( );
  131. }
  132.  
  133. x10::~x10( )
  134. {
  135.   close( this->fd );
  136. }
  137.  
  138. /*
  139.   Wait for ACK from CP-290 - return 0 if wack is ok
  140. */
  141.  
  142. int
  143. x10::wreport( )
  144. {
  145.   u_char result[7];
  146.  
  147.   read_n( this->fd, result, 7 );
  148.   {
  149.     int ok=1;
  150.  
  151.     for( int i=0; i<6; i++ ) if( result[i] != 0xff ) ok=0;
  152.  
  153.     if( !ok ) {
  154.  
  155.       printf( "Leading sync from command upload is corrupt\n" );
  156.       return 1;
  157.     }
  158.     if( !result[6] )
  159.       printf( "*** Warning: the CP-290's memory has been reset" );
  160.  
  161.     read_n( this->fd, result, 5 );
  162.  
  163.     if( this->verbose ) {
  164.  
  165.       printf( "Received report from CP-290 that something has changed\n" );
  166.     }
  167.   }
  168.   return 0;
  169. }
  170.  
  171. int
  172. x10::wack( )
  173. {
  174.   u_char result[7];
  175.  
  176.   read_n( this->fd, result, 7 );
  177.   {
  178.     int ok=1;
  179.  
  180.     for( int i=0; i<6; i++ ) if( result[i] != 0xff ) ok=0;
  181.  
  182.     if( !ok ) {
  183.  
  184.       printf("ACK from CP-290 was not received properly.  Fatal error\n" );
  185.       return 1;
  186.     }
  187.     if( !result[6] )
  188.       printf( "*** Warning: the CP-290's memory has been reset" );
  189.   }
  190.   return 0;
  191. }
  192.  
  193. /*
  194.   Dump out each byte received from the CP-290 - used for debugging
  195. */
  196.  
  197. void
  198. x10::dump( )
  199. {
  200.   u_char c;
  201.  
  202.   while( 1 ) {
  203.  
  204.     read_n( this->fd, &c, 1 );
  205.     printf( "%d\n", c );
  206.   }
  207. }
  208.  
  209. /*
  210.   Send 16 bytes of 0xff's then send a message of len bytes to the CP-290
  211. */
  212.  
  213. void
  214. x10::msg( u_char *msg, int len )
  215. {
  216.   const             NUM_RETRIES = 4;
  217.   int num_retries = NUM_RETRIES;
  218.  
  219.   static u_char sync[16]={ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  220.                            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  221.  
  222.   for( num_retries = 4; num_retries; num_retries-- ) {
  223.  
  224.     if( write_n( this->fd, sync, 16 ) == 16 ) break;
  225.     tcdrain( this->fd );
  226.     sleep(10);
  227.   }
  228.   if( ! num_retries ) printf( "Error sending sync\n" );
  229.  
  230.   for( num_retries = 4; num_retries; num_retries-- ) {
  231.         // Flush characters waiting to be read - they are from a previous
  232.         // operation or a button-press on the CP-290.
  233.     tcflush(this->fd, TCIFLUSH);
  234.  
  235.     if( write_n( this->fd, msg, len ) == len ) break;
  236.     tcdrain( this->fd );
  237.     sleep(10);
  238.   }
  239.   if( ! num_retries ) printf( "Error sending message\n" );
  240.  
  241.   tcdrain( this->fd );
  242. }
  243.  
  244. /*
  245.   Set the base housecode for the unit
  246. */
  247.  
  248. void
  249. x10::send_housecode( )
  250. {
  251.   static unsigned char hc[2]={ 0, 0 }, answer[2];
  252.  
  253.   hc[1]=this->hc;
  254.  
  255.   printf("Resetting your base housecode will erase any memory in your "
  256.          "X10 control unit.\n\n"
  257.          "Are you sure you want to set the base housecode on your "
  258.          "control unit to %c? ", this->hc_char );
  259.  
  260.   scanf( "%1s", answer );
  261.  
  262.   if( tolower( answer[0] )=='y' ) msg( hc, 2                  );
  263.   else                            printf( "Operation aborted!\n" );
  264. }
  265.  
  266. /*
  267.   Return sum of len bytes
  268. */
  269.  
  270. u_char
  271. x10::checksum( u_char *bytes, int len )
  272. {
  273.   u_char sum = 0;
  274.  
  275.   for( int i=0; i<len; i++ ) sum+=bytes[i];
  276.  
  277.   return sum;
  278. }
  279.  
  280. /*
  281.   convert string of numbers in the form of "1,2,3,4" to a bitmap appropriate
  282.   for the X10 control module
  283. */
  284.  
  285. static u_short
  286. strtobm( const char *string )
  287. {
  288. #define ITOBM(i)                (((i)<=16&&(i)>0)?(1<<(16-(i))):(0))
  289.   u_short rval=0, cur=0;
  290.  
  291.   if( string ) {
  292.  
  293.     for( int i=0; string[i]; i++ ) {
  294.  
  295.       if( isdigit( string[i] ) ) cur = cur*10 + string[i] - '0';
  296.       else {
  297.  
  298.         rval |= ITOBM( cur );
  299.         cur = 0;
  300.       }
  301.     }
  302.  
  303.     rval |= ITOBM( cur );
  304.   }
  305.  
  306.   return rval;
  307. }
  308.  
  309. static const char *
  310. bmtostr( u_short bmap )
  311. {
  312.   static char result[1024];
  313.  
  314.   strcpy( result, "0" );
  315.   int i = 0;
  316.  
  317.   if( !bmap ) return result;
  318.   else while ( bmap & 0xffffUL ) {
  319.  
  320.     i++;
  321.  
  322.     if( bmap & 0x8000UL ) {
  323.  
  324.       if( result[0] != '0' ) strcat( result, "," );
  325.       else result[0] = '\0';
  326.  
  327.       char tmp[3];
  328.       sprintf( tmp, "%d", i );
  329.       strcat( result, tmp );
  330.     }
  331.  
  332.     bmap <<= 1;
  333.   }
  334.  
  335.   return result;
  336. }
  337.  
  338. /*
  339.   Send a direct message to the CP-290
  340. */
  341.  
  342. void
  343. x10::direct( u_char command, char *str )
  344. {
  345. #define X10_MKBM_1_8(a)         (((a)&0xff00)>>8)
  346. #define X10_MKBM_9_16(a)        (((a)&0xff))
  347.  
  348.   static u_char dc[6];
  349.   u_short       bm;
  350.  
  351.   if( (bm = strtobm( str )) ) {
  352.  
  353.     if( this->verbose ) {
  354.  
  355.       char *brightness[] = {"Full brightness", "94% brightness",
  356.            "88% brightness", "80% brightness", "three fourths brightness",
  357.            "68% brightness", "62% brightness", "56% brightness", "half bright",
  358.            "43% brightness", "37% brightness", "31% brightness",
  359.            "25% brightness", "18% brightness", "12% brightness",
  360.            "06% brightness", "Totally dark (you might as well turn it off!)" };
  361.  
  362.       switch( command & 15 ) {
  363.  
  364.         case X10_DIM:
  365.           printf( "Dimming to %s: ", brightness[( command&0xf0 ) >> 4] );
  366.           break;
  367.  
  368.         case X10_ON : 
  369.           printf( "Turning on: " );
  370.           break;
  371.  
  372.         case X10_OFF:
  373.           printf( "Turning off: " );
  374.           break;
  375.       }
  376.  
  377.       for( int i=0;i<16;i++ ) {
  378.         if( bm & ( ITOBM( i ) ) ) printf( "%d,", i );
  379.       }
  380.       printf( "\b \n" );
  381.     }
  382.  
  383.     {
  384.       const int cmd = command&15;
  385.  
  386.       if( cmd != X10_DIM && cmd != X10_ON && cmd != X10_OFF ) {
  387.         printf( "Attempt to perform unknown command!\n" );
  388.         return;
  389.       }
  390.     }
  391.  
  392.     dc[0] = 1; /* Direct command */
  393.     dc[1] = command;
  394.     dc[2] = this->hc;
  395.     dc[3] = X10_MKBM_9_16( bm );
  396.     dc[4] = X10_MKBM_1_8( bm );
  397.     dc[5] = checksum( dc+1, 4 );
  398.  
  399.     msg( dc, 6 );
  400.   }
  401.   wack( );
  402.   wreport( );
  403. }
  404.  
  405. /*
  406.   Configure the serial port that the CP-290 is attached to
  407. */
  408.  
  409. void
  410. x10::config_serial( )
  411. {
  412.   static struct termios tios;
  413.  
  414.   if( ! this->fd ) {
  415.  
  416.     printf("Must set_fd() before config_serial()");
  417.     return;
  418.   }
  419.  
  420.   tcgetattr( this->fd, &tios );
  421.  
  422.   tios.c_iflag &= ~( BRKINT | IGNPAR | PARMRK | INPCK |
  423.                      ISTRIP | INLCR  | IGNCR  | ICRNL | IXON );
  424.   tios.c_iflag |=    IGNBRK | IXOFF;
  425.  
  426.   tios.c_lflag &= ~( ECHO   | ECHOE  | ECHOK  | ECHONL |
  427.                      ICANON | ISIG   | NOFLSH | TOSTOP );
  428.  
  429.   tios.c_oflag &= ~( OFILL );
  430.   tios.c_oflag |= CR3 | NL1;
  431.  
  432.   tios.c_cflag &= ~(  CRTSCTS | CSIZE | CSTOPB | HUPCL  | PARENB );
  433.   tios.c_cflag |=    CLOCAL | CREAD  | CS8 ;
  434.  
  435.   cfsetospeed( &tios, B600 );
  436.   cfsetispeed( &tios, B600 );
  437.  
  438.   tcsetattr( this->fd, TCSAFLUSH, &tios );
  439. }
  440.  
  441. /*
  442.   Explain a little bit of how to use this app
  443. */
  444.  
  445. static void
  446. usage( char *name )
  447. {
  448.   printf( "x10 "VERSION" control program "
  449.          "by Aaron Hightower (aaron@paradigmsim.com)\n"
  450.          "=== Usage ===\n"
  451.          "%s: [-v] [-c housecode] [-n list] [-f list] [-d dimlevel,list]\n\n"
  452.          "-v : verbose , -t : self-test, -q : query CP-290's day and time\n"
  453.          "-s : set CP-290's time according to CPU's day and time\n"
  454.          "-z [a-p] : set the rocker button housecode for the CP290\n"
  455.          "-c [a-p] : use alternate house code (default \"a\")\n"
  456.          "-n list : turn oN devices in list\n"
  457.          "-f list : turn oFf devices in list\n"
  458.          "-e : empty conents of CP-290's internal events\n"
  459.          "-l : list contents of CP-290 to stdout\n"
  460.          "-d dimlevel,list : dim devices in list to dimlevel\n"
  461.          "list is a comma separated list of devices, each ranging from 1 to 16"
  462.          "\ndimlevel is an integer from 0 to 15 (0 brightest)\n"
  463.          "\nExamples:\n"
  464.          "\tTurn on devices a1 a2 a3: \"%s -n 1,2,3 \"\n"
  465.          "\tTurn off devices b4 b6  : \"%s -c b -f 4,6\"\n"
  466.          "\tdim lights 2 and 3 to 5th brightest setting: \"%s -d 5,2,3\"\n\n"
  467.          "Note: All commands use house code \"a\" unless you use the \"-c\" "
  468.          "argument.\nThe house code set by the \"-z\" command is for the "
  469.          "buttons on the CP-290.\n"
  470.           , name, name, name, name );
  471.   exit( 0 );
  472. }
  473.  
  474. /*
  475.   Issue the selftest command to the CP-290 and report what it says
  476. */
  477.  
  478. void
  479. x10::selftest( )
  480. {
  481.   u_char stest=7, result[7];
  482.  
  483.   printf( "Performing self-test (This will take about 10 seconds)\n" );
  484.   msg( &stest, 1 );
  485.   read_n( this->fd, result, 7 );
  486.  
  487.   {
  488.     int i, ok = 1;
  489.  
  490.     for( i=0; i<6; i++ ) if( result[i] != 0xff ) ok = 0;
  491.     if( result[6]!=0 ) ok = 0;
  492.  
  493.     if( ok ) printf( "Everything is okay!\n" );
  494.     else     printf( "Sorry, something is wrong\n" );
  495.   }
  496. }
  497.  
  498. /*
  499.   Set the time of the CP-290 based on the time of the CPU
  500. */
  501.  
  502. void
  503. x10::settime( )
  504. {
  505.   char  *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  506.   u_char dow[7] = { 64, 1, 2, 4, 8, 16, 32 };
  507.   u_char set[5] = { 2, 30, 7, 1, 0 };
  508.   struct tm *tp;
  509.   time_t secs;
  510.  
  511.   secs = time( NULL );
  512.   tp = localtime( &secs );
  513.  
  514.   set[2] = tp->tm_hour;
  515.   set[1] = tp->tm_min;
  516.   set[3] = dow[tp->tm_wday];
  517.  
  518.   printf( "Setting time to %s %d:%02d\n", days[tp->tm_wday], set[2], set[1] );
  519.   set[4] = checksum( set+1, 3 );
  520.   msg( set, 5 );
  521. }
  522.  
  523. /*
  524.   Find out what the time of the CP-290 is currently set to
  525. */
  526.  
  527. void
  528. x10::gettime( )
  529. {
  530.   u_char ask=4, result[12];
  531.   char *day;
  532.  
  533.   msg( &ask,    1 );
  534.   read_n( this->fd, result, 12 );
  535.   msg( &ask,    1 );
  536.   read_n( this->fd, result, 12 );
  537.  
  538.   switch( result[9] ) {
  539.  
  540.     case 64: day = "Sun";break;
  541.     case 32: day = "Sat";break;
  542.     case 16: day = "Fri";break;
  543.     case  8: day = "Thu";break;
  544.     case  4: day = "Wed";break;
  545.     case  2: day = "Tue";break;
  546.     case  1: day = "Mon";break;
  547.     default: day = "???";break;
  548.   }
  549.  
  550.   printf( "The CP-290's internal clock is set to: %s, %d:%02d\n",
  551.     day, result[8], result[7] );
  552. }
  553.  
  554. void
  555. x10::readtab( )
  556. {
  557.   int    i;
  558.   int    j;
  559.   u_char result[8],
  560.          ask=5;
  561.  
  562.   for ( j = 0; j < 6; j++ ) {
  563.           // This seems to fail sometimes because of a read-buffer overrun.
  564.           // flush the read buffer and start over, and hope I get all of the
  565.           // characters this time.
  566.           tcflush(this->fd, TCIOFLUSH);
  567.           msg( &ask, 1   );
  568.           if( -1 == read_n( this->fd, result, 7 ) )
  569.                 continue;
  570.  
  571.           for( i=0; i<6; i++ ) {
  572.                         if( result[i] != 255 )
  573.                                 continue;
  574.           }
  575.           break;
  576.           sleep(5);
  577.   }
  578.   if ( j == 7 ) {
  579.         fprintf( stderr, "Did not receive sync from CP-290\n" );
  580.         return;
  581.   }
  582.   if( result[6] == 0 ) {
  583.  
  584.     fprintf( stderr, "Warning!  CP-290 has been reset.  All memory has been lost\n" );
  585.     listvalid = 1;
  586.     return;
  587.   }
  588.  
  589.   for( i=0; i<128; i++ ) {
  590.     fprintf( stderr, "Reading slot %d .. ", i );
  591.  
  592.     read_n( this->fd, & events[i][0], 1 );
  593.  
  594.     if( events[i][0] != 255 ) {
  595.       read_n( this->fd, & events[i][1], 7 );
  596.       fprintf( stderr, "\n" );
  597.     }
  598.     else fprintf( stderr, "empty\n" );
  599.   }
  600.  
  601.   listvalid = 1;
  602.  
  603.   fprintf( stderr, "\n" );
  604. }
  605.  
  606. void
  607. x10::emptytab( void )
  608. {
  609.   fprintf( stderr, "Erasing all of CP-290's internal memory\n" );
  610.  
  611.   bzero( events, sizeof( events ) );
  612.  
  613.   writetab( 1 );
  614. }
  615.  
  616. void
  617. x10::writetab( int empty )
  618. {
  619.   event e;
  620.  
  621.   e.id = 3;
  622.  
  623.   for( int i=0; i<128; i++ ) {
  624.  
  625.     if( events[i][4] == 0 && events[i][5] == 0 && empty != 1 ) continue;
  626.  
  627.     printf( "Writing timer event %d\n", i );
  628.  
  629.     printevent( i );
  630.  
  631.     e.ev_num[0] = ( i << 3 ) & 0xfc;
  632.     e.ev_num[1] = ( i >> 5 ) & 0x03;
  633.     e.mode      = events[i][0];
  634.     e.daymap    = events[i][1];
  635.     e.hour      = events[i][2];
  636.     e.minute    = events[i][3];
  637.     e.devbm[0]  = events[i][4];
  638.     e.devbm[1]  = events[i][5];
  639.     e.housecode = events[i][6];
  640.     e.lev_fun   = events[i][7];
  641.  
  642.     e.checksum = checksum( ( unsigned char * ) &e, sizeof( event ) - 1 );
  643.  
  644.     msg( ( unsigned char * ) &e, sizeof( event ) );
  645.   }
  646. }
  647.  
  648. void
  649. x10::printevent( int i )
  650. {
  651.   if( events[i][0] != 255 ) {
  652.  
  653.     int mode, daymap, hour, minute, devbm, housecode, level, function;
  654.  
  655.     mode      = events[i][0] & 0x0f;
  656.     daymap    = events[i][1] & 0x7f;
  657.     hour      = events[i][2] & 0x1f;
  658.     minute    = events[i][3] & 0x3f;
  659.     devbm     = ( ( ( int )events[i][4] ) << 8 ) + ( ( int ) events[i][5] );
  660.     housecode = events[i][6] >> 4;
  661.     level     = events[i][7] >> 4;
  662.     function  = events[i][7] & 0x0f;
  663.  
  664.     char *modedesc;
  665.  
  666.     switch( mode ) {
  667.  
  668.       case X10_MODE_NORMAL:   modedesc = "normal";   break;
  669.       case X10_MODE_SECURITY: modedesc = "security"; break;
  670.       case X10_MODE_TODAY:    modedesc = "today";    break;
  671.       case X10_MODE_TOMORROW: modedesc = "tomorrow"; break;
  672.       default:                modedesc = "normal";   break;
  673.     }
  674.  
  675.     char *funcdesc;
  676.  
  677.     switch( function ) {
  678.  
  679.       case X10_ON:  funcdesc = "on";  break;
  680.       case X10_OFF: funcdesc = "off"; break;
  681.       case X10_DIM: funcdesc = "dim"; break;
  682.       default:      funcdesc = "on";  break;
  683.     }
  684.  
  685.     switch( housecode ) {
  686.  
  687.       case 0xe: housecode = 'b'; break;
  688.       case 0x2: housecode = 'c'; break;
  689.       case 0xa: housecode = 'd'; break;
  690.       case 0x1: housecode = 'e'; break;
  691.       case 0x9: housecode = 'f'; break;
  692.       case 0x5: housecode = 'g'; break;
  693.       case 0xd: housecode = 'h'; break;
  694.       case 0x7: housecode = 'i'; break;
  695.       case 0xf: housecode = 'j'; break;
  696.       case 0x3: housecode = 'k'; break;
  697.       case 0xb: housecode = 'l'; break;
  698.       case 0x0: housecode = 'm'; break;
  699.       case 0x8: housecode = 'n'; break;
  700.       case 0x4: housecode = 'o'; break;
  701.       case 0xc: housecode = 'p'; break;
  702.       case 0x6: // House code 'a' doesn't print since it's the default
  703.       default:  housecode =  0 ; break;
  704.     }
  705.  
  706.                     printf( "event {\n" );
  707.                     printf( "  devmap    %s\n", bmtostr( devbm ) );
  708.                     printf( "  daymap    %s\n", bmtostr( daymap << 9 ) );
  709.                     printf( "  function  %s\n", funcdesc );
  710.                     printf( "  hour      %d\n", hour );
  711.     if( minute    ) printf( "  minute    %d\n", minute );
  712.                     printf( "  mode      %s\n", modedesc );
  713.     if( housecode ) printf( "  housecode %c\n", housecode );
  714.     if( level     ) printf( "  dimlevel  %d\n", level );
  715.  
  716.     printf( "}\n\n" );
  717.   }
  718. }
  719.  
  720. /*
  721.   List out the events that have been set in the CP-290 memory
  722.   Assumes table already defined
  723. */
  724.  
  725. void
  726. x10::listtab( )
  727. {
  728.   for( int i=0; i<128; i++ ) {
  729.  
  730.     printevent( i );
  731.   }
  732.  
  733.   printf( "\n" );
  734. }
  735.  
  736. /*
  737.   Turn on verbose mode printing
  738. */
  739.  
  740. void
  741. x10::verbose_on( )
  742. {
  743.   this->verbose = 1;
  744. }
  745.  
  746. /*
  747.   Change which file descriptor to use for direct commands, etc
  748. */
  749.  
  750. void
  751. x10::setFD( int _fd )
  752. {
  753.   this->fd = _fd;
  754. }
  755.  
  756. static int
  757. chartohc( char c )
  758. {
  759.   static char *housecodes = "meckogainfdlphbj";
  760.  
  761.   c = tolower( c );
  762.  
  763.   if( c<'a' || c>'p' ) c = 'a';
  764.  
  765.   return ( ( strlen( housecodes ) - strlen( index( housecodes, c ) ) ) <<4 );
  766. }
  767.  
  768. /*
  769.   Change which housecode to use for direct commands, etc
  770. */
  771.  
  772. void
  773. x10::housecode( int c )
  774. {
  775.   c = tolower( c );
  776.  
  777.   if( c<'a' || c>'p' ) c = 'a';
  778.  
  779.   this->hc_char = c;
  780.   this->hc      = chartohc( c );
  781. }
  782.  
  783. void
  784. x10::readtab( const char *filename )
  785. {
  786.   char buffer[1024];
  787.   FILE *file;
  788.  
  789.   file = fopen( filename, "r" );
  790.  
  791.   int i=0;
  792.  
  793.   char *housecode = 0;
  794.   char *devmap    = 0;
  795.   char *daymap    = 0;
  796.   char *mode      = 0;
  797.   int   minute    = 0;
  798.   int   hour      = 0;
  799.   int   dimlevel  = 0;
  800.   char *func      = 0;
  801.  
  802.   while( ! feof( file ) ) {
  803.  
  804.     if( NULL == fgets( buffer, 1023, file ) ) break;
  805.  
  806.     if( buffer[0] == '#' ) continue;
  807.  
  808.     char *word = strtok( buffer, " \t\n" );
  809.  
  810.     while( word ) {
  811.  
  812.       // Support for hash comments
  813.       if( word[0] == '#' ) break;
  814.  
  815.       // Support for C++ comments
  816.       else if( word[0] == '/' && word[1] == '/' ) break; 
  817.  
  818.       if( !strcmp( word, "event" ) ) {
  819.  
  820.         word = strtok( NULL, " \t\n" );
  821.  
  822.         if( strcmp( word, "{" ) ) { /* } */
  823.  
  824.           fprintf( stderr, "Expecting an open brace" );
  825.         }
  826.       }
  827.       else if( !strcmp( word, "housecode" ) ) housecode = strdup( word = strtok( NULL, " \t\n" ) );
  828.       else if( !strcmp( word, "devmap"    ) ) devmap    = strdup( word = strtok( NULL, " \t\n" ) );
  829.       else if( !strcmp( word, "daymap"    ) ) daymap    = strdup( word = strtok( NULL, " \t\n" ) );
  830.       else if( !strcmp( word, "mode"      ) ) mode      = strdup( word = strtok( NULL, " \t\n" ) );
  831.       else if( !strcmp( word, "minute"    ) ) minute    = atoi  ( word = strtok( NULL, " \t\n" ) );
  832.       else if( !strcmp( word, "hour"      ) ) hour      = atoi  ( word = strtok( NULL, " \t\n" ) );
  833.       else if( !strcmp( word, "dimlevel"  ) ) dimlevel  = atoi  ( word = strtok( NULL, " \t\n" ) );
  834.       else if( !strcmp( word, "function"  ) ) func      = strdup( word = strtok( NULL, " \t\n" ) );
  835.  
  836.       else if( !strcmp( word, "}" ) ) {
  837.  
  838.         int intfunc;
  839.  
  840.         if( !func ) intfunc = X10_ON;
  841.         else if( !strcasecmp( func, "on"  ) ) intfunc = X10_ON;
  842.         else if( !strcasecmp( func, "off" ) ) intfunc = X10_OFF;
  843.         else if( !strcasecmp( func, "dim" ) ) intfunc = X10_DIM;
  844.         else intfunc = X10_ON;
  845.  
  846.         if( intfunc != X10_DIM ) dimlevel = 0;
  847.  
  848.         int intmode;
  849.  
  850.              if( !strcasecmp( func, "normal"   ) ) intmode = X10_MODE_NORMAL;
  851.         else if( !strcasecmp( func, "security" ) ) intmode = X10_MODE_SECURITY;
  852.         else if( !strcasecmp( func, "today"    ) ) intmode = X10_MODE_TODAY;
  853.         else if( !strcasecmp( func, "tomorrow" ) ) intmode = X10_MODE_TOMORROW;
  854.         else if( !strcasecmp( func, "clear"    ) ) intmode = X10_MODE_CLEAR;
  855.         else intmode = X10_MODE_NORMAL;
  856.  
  857.         if( housecode ) printf( "housecode = %s\n", housecode );
  858.         if( devmap    ) printf( "devmap    = %s\n", devmap    );
  859.         if( daymap    ) printf( "daymap    = %s\n", daymap    );
  860.         if( mode      ) printf( "mode      = %s\n", mode      );
  861.         if( minute    ) printf( "minute    = %d\n", minute    );
  862.         if( hour      ) printf( "hour      = %d\n", hour      );
  863.         if( func      ) printf( "function  = %s\n", func      );
  864.  
  865.         if( dimlevel )
  866.           printf( "dimlevel  = %d\n", dimlevel  );
  867.  
  868.         printf( "\n" );
  869.  
  870.         setEvent( i++, housecode ? housecode[0] : 'a',
  871.                   devmap, daymap, intmode, minute, hour,
  872.                   dimlevel, intfunc );
  873.  
  874.         if( housecode ) free( housecode );
  875.         if( devmap    ) free( devmap    );
  876.         if( daymap    ) free( daymap    );
  877.         if( func      ) free( func      );
  878.         if( mode      ) free( mode      );
  879.  
  880.         housecode = 0;
  881.         devmap    = 0;
  882.         daymap    = 0;
  883.         mode      = 0;
  884.         minute    = 0;
  885.         hour      = 0;
  886.         dimlevel  = 0;
  887.         func      = 0;
  888.       }
  889.  
  890.       word = strtok( NULL, " \t\n" );
  891.     }
  892.   }
  893. }
  894.  
  895. void
  896. x10::setEvent( int i, char housecode, const char *devmap, const char *daymap,
  897.                int mode, int minute, int hour, int dimlevel, int func )
  898. {
  899.   events[i][0] = ( mode   & 0x0f );
  900.   events[i][1] = ( ( strtobm( daymap ? daymap : "1,2,3,4,5,6,7" ) ) >> 9 );
  901.   events[i][2] = ( hour   & 0x1f );
  902.   events[i][3] = ( minute & 0x3f );
  903.   events[i][4] = ( strtobm( devmap ? devmap : "1" ) >> 8 ) & 0xff;
  904.   events[i][5] = ( strtobm( devmap ? devmap : "1" ) >> 0 ) & 0xff;
  905.   events[i][6] = chartohc( housecode );
  906.   events[i][7] = ( ( dimlevel & 0xf ) << 4 ) | ( func & 0xf );
  907. }
  908.  
  909. /*
  910.   Main
  911. */
  912.  
  913. int
  914. main( int argc, char * const argv[] )
  915. {
  916.   if( argc <= 1 ) {usage( argv[0] );exit( 0 );}
  917.  
  918.   char *portname = ( char * )getenv( "X10_PORTNAME" );
  919.  
  920.   if( !portname ) portname = "/dev/x10";
  921.  
  922.   int fd;
  923.  
  924.   if( ( fd = open( portname, O_RDWR|O_NOCTTY, 0 ) ) == -1 ) {
  925.  
  926.     printf( "Could not access serial port %s\nPlease set environment variable "
  927.            "X10_PORTNAME to proper serial device\n"
  928.            "or else make the appropriate soft link as superuser.\n", portname );
  929.     exit( -1 );
  930.   }
  931.  
  932.   x10 *x = new x10( fd );
  933.  
  934.   x->housecode('a');
  935.  
  936.   int c;
  937.  
  938.   while( ( c = getopt( argc, argv, "z:d:elvhc:n:f:qst" ) ) != -1 ) {
  939.  
  940.     switch( c ) {
  941.  
  942.       case 'z':
  943.         x->housecode( optarg?( *optarg ):'a' );
  944.         x->send_housecode( );
  945.         break;
  946.  
  947.       case 'v':
  948.         x->verbose_on( );
  949.         break;
  950.  
  951.       case 'c':
  952.         x->housecode( optarg ? ( *optarg ) : 'a' );
  953.         break;
  954.  
  955.       case 'd':
  956.         if( optarg ) { /* First number is the dimlevel (0..15) */
  957.           int dimlevel;
  958.           char *dim;
  959.  
  960.           dim = strdup( optarg );
  961.           sscanf( optarg, "%d,%s", &dimlevel, dim );
  962.           x->direct( X10_DIM + ( ( dimlevel & 0xf ) << 4 ), dim );
  963.         }
  964.         break;
  965.  
  966.       case 'e':
  967.         x->emptytab( );
  968.         break;
  969.  
  970.       case 'l':
  971.         x->readtab( );
  972.         x->listtab( );
  973.         break;
  974.  
  975.       case 'n':
  976.         x->direct( X10_ON, optarg );
  977.         break;
  978.  
  979.       case 'f':
  980.         x->direct( X10_OFF, optarg );
  981.         break;
  982.  
  983.       case 't':
  984.         x->selftest( );
  985.         break;
  986.  
  987.       case 's':
  988.         x->settime( );
  989.         x->gettime( );
  990.         break;
  991.  
  992.       case 'q':
  993.         x->gettime( );
  994.         break;
  995.  
  996.       case 'h':
  997.       case '?':
  998.         usage( argv[0] );
  999.         break;
  1000.     }
  1001.   }
  1002.  
  1003.   if (optind < argc)
  1004.   {
  1005.     if( optind == argc - 1 ) {
  1006.  
  1007.       x->readtab( argv[ optind ] );
  1008.       x->listtab();
  1009.       x->writetab();
  1010.     }
  1011.   }
  1012.  
  1013.   exit( 0 );
  1014.  
  1015.   return 0;
  1016. }
  1017.